﻿// -----------------------------------------------------------------------
// <copyright file="ConfigReader.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

namespace GenericMethodInvoker.XmlConfigReader
{
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Xml;

    /// <summary>
    /// Reads the config and loads the Scenarios defined
    /// </summary>
    public static class ConfigReader
    {
        /// <summary>
        /// Namespace URI Constant
        /// </summary>
        private const string XsdNamespaceUri = "http://microsoft.com/mgsi/sunil/GenericMethodInvoker.xsd";

        /// <summary>
        /// XML document that represents the config
        /// </summary>
        private static readonly XmlDocument Configdoc = new XmlDocument();

        /// <summary>
        /// NamespaceManager for the XMLDocument
        /// </summary>
        private static readonly XmlNamespaceManager NamespaceManager = new XmlNamespaceManager(Configdoc.NameTable);

        /// <summary>
        /// Initializes static members of the <see cref="ConfigReader"/> class.
        /// </summary>
        static ConfigReader()
        {
            NamespaceManager.AddNamespace("def", XsdNamespaceUri);
        }
        
        /// <summary>
        /// Gets the scenarios.
        /// </summary>
        public static List<Scenario> Scenarios
        {
            get
            {
                return ReadConfig();
            }
        }

        /// <summary>
        /// Reads the config.
        /// </summary>
        /// <returns>List of Scenarios</returns>
        private static List<Scenario> ReadConfig()
        {
            var scenarios = new List<Scenario>();

            var doc = new XmlDocument();
            doc.Load(ConfigurationManager.AppSettings.Get("ConfigFile"));

            var scenarioNodes = doc.SelectNodes("/def:scenarios/def:scenario", NamespaceManager);
            if (scenarioNodes != null)
            {
                scenarios.AddRange(from XmlNode scenarioNode in scenarioNodes
                                   let xmlAttributeCollection = scenarioNode.Attributes
                                   where xmlAttributeCollection != null
                                   select new Scenario
                                              {
                                                  Name = xmlAttributeCollection["name"].Value,
                                                  Include = xmlAttributeCollection["include"] != null && bool.Parse(xmlAttributeCollection["include"].Value),
                                                  ObjectInstances = GetObjectInstanceConfigs(scenarioNode)
                                              });
            }

            return scenarios;
        }

        /// <summary>
        /// Gets the object instance configurations.
        /// </summary>
        /// <param name="objectInstanceNodesContainer">The object instance nodes.</param>
        /// <returns>
        /// List of ObjectInstance
        /// </returns>
        private static List<ObjectInstance> GetObjectInstanceConfigs(XmlNode objectInstanceNodesContainer)
        {
            var objectInstances = new List<ObjectInstance>();
            if (objectInstanceNodesContainer != null)
            {
                var objectInstanceNodes = objectInstanceNodesContainer.SelectNodes("def:objectInstance", NamespaceManager);
                if (objectInstanceNodes != null)
                {
                    foreach (XmlNode objectNode in objectInstanceNodes)
                    {
                        var objectInstance = new ObjectInstance();
                        if (objectNode.Attributes != null)
                        {
                            objectInstance.ObjectName = objectNode.Attributes["name"].Value;
                            objectInstance.ObjectType = objectNode.Attributes["type"].Value;

                            objectInstance.ObjectValue = objectNode.Attributes["value"] != null
                                                      ? objectNode.Attributes["value"].Value
                                                      : null;
                            objectInstance.ObjectAssembly = objectNode.Attributes["assembly"] != null
                                                      ? objectNode.Attributes["assembly"].Value
                                                      : null;
                            
                            var initializeThroughConstructor = false;
                            if (objectNode.Attributes["initializeThroughConstructor"] != null)
                            {
                                bool.TryParse(objectNode.Attributes["initializeThroughConstructor"].Value, out initializeThroughConstructor);
                            }

                            objectInstance.InitializeThroughConstructor = initializeThroughConstructor;

                            var isStatic = false;
                            if (objectNode.Attributes["isStatic"] != null)
                            {
                                bool.TryParse(objectNode.Attributes["isStatic"].Value, out isStatic);
                            }

                            objectInstance.IsStatic = isStatic;
                        }

                        objectInstance.MethodCalls = GetMethodCallConfigs(objectNode);
                        objectInstance.Properties = GetObjectInstanceConfigs(objectNode.SelectSingleNode("def:properties", NamespaceManager));
                        objectInstance.ConstructorParams = GetObjectInstanceConfigs(objectNode.SelectSingleNode("def:constructorParameters", NamespaceManager));

                        objectInstances.Add(objectInstance);
                    }
                }
            }

            return objectInstances;
        }

        /// <summary>
        /// Gets the method call configurations.
        /// </summary>
        /// <param name="objectNode">The object node.</param>
        /// <returns>List of MethodCalls</returns>
        private static List<MethodCall> GetMethodCallConfigs(XmlNode objectNode)
        {
            var methodCalls = new List<MethodCall>();

            var methodCallNodes = objectNode.SelectNodes("def:methods/def:methodCall", NamespaceManager);
            if (methodCallNodes != null)
            {
                foreach (XmlNode methodCallNode in methodCallNodes)
                {
                    if (methodCallNode.Attributes != null)
                    {
                        var methodCall = new MethodCall
                                             {
                                                 MethodName = methodCallNode.Attributes["name"].Value
                                             };

                        var include = false;
                        if (objectNode.Attributes != null)
                        {
                            bool.TryParse(methodCallNode.Attributes["include"].Value, out include);
                        }

                        methodCall.Include = include;

                        methodCall.Iterations = GetMethodCallIterationConfigs(methodCallNode);

                        methodCalls.Add(methodCall);
                    }
                }
            }

            return methodCalls;
        }

        /// <summary>
        /// Gets the method call iteration configurations.
        /// </summary>
        /// <param name="methodCallNode">The method call node.</param>
        /// <returns>List of Iterations</returns>
        private static List<Iteration> GetMethodCallIterationConfigs(XmlNode methodCallNode)
        {
            var iterationNodes = methodCallNode.SelectNodes("def:iterationParameters", NamespaceManager);

            return (from XmlNode iterationNode in iterationNodes
                    select new Iteration
                               {
                                   IterationCultureName = iterationNode.Attributes != null && iterationNode.Attributes["culture"] != null 
                                   ? iterationNode.Attributes["culture"].Value 
                                   : null, 

                                   IterationRepeatCount = iterationNode.Attributes != null && iterationNode.Attributes["repeatIterationCount"] != null 
                                   ? int.Parse(iterationNode.Attributes["repeatIterationCount"].Value) 
                                   : 1, 

                                   Parameters = GetObjectInstanceConfigs(iterationNode)
                               }).ToList();
        }
    }
}
